<?php
    /*

     Version: MPL 1.1

     Software distributed under the License is distributed on an "AS IS"
     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
     License for the specific language governing rights and limitations
     under the License.

     The Original Code is KASSIOP Framework.

     The Initial Developer of the Original Code is SURIAN Nicolas (aka NairuS) <me@nairus.fr>.
     Portions created by the Initial Developer are Copyright (C) 2010
     The Initial Developer. All Rights Reserved.

     Contributor(s):

     Alternatively, the contents of this file may be used under the terms
     of the MPL license Version 1.1 (the  "MPL" License"), in which case the
     provisions of Version: MPL 1.1 License are applicable instead of those
     above.  If you wish to allow use of your version of this file only
     under the terms of the MPL License and not to allow others to use
     your version of this file under the MPL, indicate your decision by
     deleting  the provisions above and replace  them with the notice and
     other provisions required by the MPL License.  If you do not delete
     the provisions above, a recipient may use your version of this file
     under either the MPL License.

     The contents of this file are subject to the Mozilla Public License
     Version 1.1 (the "License"); you may not use this file except in
     compliance with the License. You may obtain a copy of the License at
     http://www.mozilla.org/MPL/

    */

    /**
     * Abstract class witch provides mysql connection.
     *
     * @package    kassiop.net
     * @subpackage remoting
     *
     * @author NairuS
     */
    abstract class ServicesMysql implements Findable
    {
        /**
         * Create an instance of ServicesMysql
         *
         * @param $file initialize an other mysql configuration.
         */
        public function __construct( $file = null )
        {
            // get mysql config.
            $this->config  = new ConfigManager( $file ) ;
            $mysqlConfig   = new MysqlConfig( $this->config );

            // init mysql connexion.
            $this->connect = new MysqlConnector( $mysqlConfig->host , $mysqlConfig->user , $mysqlConfig->pass , $mysqlConfig->name ) ;

            // build session name.
            Configuration::includeBootstrap();
            $projectName       = $this->config->get( APPLICATIONS_PROJECT_NAME_PROPERTY, APPLICATIONS_SECTION );
            $appName           = $this->config->get( NAME_PROPERTY, FRONTEND_SECTION );
            $this->sessionName = $projectName != '' ? $projectName . '.' . $appName : $appName ;
        }

        /**
         * Defines the AND SQL condition.
         *
         * @var string
         */
        const _AND_ = "AND" ;


        /**
         * Defines the OR SQL condition.
         *
         * @var string
         */
        const _OR_ = "OR" ;

        /**
         * The ascendent direction of the ORDER BY query part.
         *
         * @var string
         */
        const ASC = "ASC" ;

        /**
         * The descendent direction of the ORDER BY query part.
         *
         * @var string
         */
        const DESC = "DESC" ;

        /**
         * Defiens the session name.
         *
         * @var string
         */
        public $sessionName ;

        /**
         * Store the mysql config
         *
         * @var ConfigManager
         */
        protected $config ;

        /**
         * Store the mysql connection
         *
         * @var MysqlConnector
         */
        protected $connect ;

        /**
         * Store the creadential manager
         *
         * @var CredentialManager
         */
        protected $credentialManager ;

        /**
         * Store the current RoleVO of the user.
         *
         * @var RoleVO
         */
        protected $role ;

        /**
         * Defines the table by default to use.
         * @var string
         */
        protected $table ;

        /**
         * Defines all the true values sent.
         *
         * @var array
         */
        protected static $trueValues = array( 1 , 'yes' , 'y' , 'true' , 't' , 'on', 'o' );

        /**
         * Builds the query to launch after the validation of the model.
         *
         * @param  string      $query
         * @param  ValueObject $vo
         * @return string
         */
        public function buildQuery( $query, ValueObject $vo )
        {
            $pattern    = '{%s}';
            $reflection = new ReflectionClass( $vo );

            // for each public properties, replace the pattern by the value in the query.
            foreach( $reflection->getProperties( ReflectionProperty::IS_PUBLIC ) as $property )
            {
                $name  = $property->getName();
                $query = str_replace( sprintf( $pattern, $name ), $vo->$name, $query );
            }
            return $query;
        }

        /**
         * Return the ValueObject for the id passed-in.
         *
         * @param  mixed       $id integer or string identifier of the object.
         * @return ValueObject
         */
        public function find( $id )
        {
            if( $this->isAuthorized( $this->buildCredential( __FUNCTION__ ) ) )
            {
                $query  = "SELECT * FROM " . $this->table . " WHERE " . $this->table . ".`id` = '" . $this->sanitize( $id ) . "'" ;
                $result = $this->getResult( $query ) ;
                return $this->getVO( mysql_fetch_array( $result ) ) ;
            }
            else
            {
                return new NetServerInfoVO
                (
                    'is_not_authorized',
                    'You have the rights to use this service.',
                    NetServerLevel::ERROR,
                    __LINE__,
                    __FUNCTION__,
                    get_class( $this )
                ) ;
            }
        }

        /**
         * Return an array of ValueObject.
         *
         * @param  array  $conditions     defines the where conditions of the query.
         * @param  array  $orders         defines the orderBy conditions of the query.
         * @param  string $orderDirection defines the orderBy direction of the query.
         * @return array                  of ValueObject.
         */
        function findAll( $conditions = array(), $orders = array(), $orderDirection = ServicesMysql::ASC )
        {
            if( $this->isAuthorized( $this->buildCredential( __FUNCTION__ ) ) )
            {
                $where   = empty( $conditions ) ? null : $this->buildWhere( $conditions ) ;
                $orderBy = empty( $orders )     ? null : $this->buildOrderBy( $orders, $orderDirection );
                $query   = "SELECT * FROM " . $this->table . $where . $orderBy . ";" ;
                $result  = $this->getResult( $query ) ;

                 $stack = array() ;
                if( mysql_num_rows($result) > 0 )
                {
                    while( $row = mysql_fetch_array( $result ) )
                    {
                        array_push( $stack, $this->getVO( $row ) ) ;
                    }
                }
                return $stack ;
            }
            else
            {
                return new NetServerInfoVO
                (
                    'is_not_authorized',
                    'You have the rights to use this service.',
                    NetServerLevel::ERROR,
                    __LINE__,
                    __FUNCTION__,
                    get_class( $this )
                ) ;
            }
        }

        /**
         * Returns an array formatted for a list.
         * Override this method if we want differents fields.
         *
         * @return array
         */
        public function generateList()
        {
            $stack = array();
            if( $this-> isAuthorized( $this->buildCredential( __FUNCTION__ ) ) )
            {
                $list = $this->findAll() ;
                if( is_array( $list ) && !empty( $list ) )
                {
                    foreach( $list as $vo )
                    {
                        // if the vo return is inherated of LabelVO
                        if( $vo instanceof LabelVO )
                        {
                            $stack[$vo->id] = $vo->label ;
                        }
                    }
                }
            }
            return $stack ;
        }

        /**
         * Sanitizes all values before to send sql request.
         *
         * @param  mixed $row
         * @return mixed
         */
        public function sanitize( $row )
        {
            $cleaned = null ;
            if( is_array( $row ) )
            {
                $cleaned = array();
                foreach( $row as $key => $value )
                {
                    $cleaned[$key] = StringsHelper::sqlSanitize( $value ) ;
                }
            }
            elseif( is_string( $row ) )
            {
                $cleaned = StringsHelper::sqlSanitize( $row ) ;
            }
            elseif( is_object( $row ) )
            {
                $cleaned = $row ;
                foreach( $row as $prop => $value )
                {
                    $cleaned->$prop = StringsHelper::sqlSanitize( $value ) ;
                }
            }
            else
            {
                $cleaned = $row ;
            }
            return $cleaned ;
        }

        /**
         * Build the credential with the current class.
         *
         * @param  string $method
         * @return string
         */
        protected function buildCredential( $method )
        {
            return get_class( $this ) . '.' . $method;
        }

        /**
         * Build the SELECT part of the query.
         *
         * @param  array  $fields The fields name for the select query part
         * @return string         The SELECT part of the query.
         */
        protected function buildSelect( array $fields )
        {
            $queryPart = "SELECT ";
            $i         = 0;
            foreach( $fields as $field => $value )
            {
                $sep        = $i > 0 ? ", " : "" ;
                $queryPart .= $sep . "`" . $field . "`" ;
                $i++ ;
            }
            $queryPart .= " FROM " . $this->table ;
            return $queryPart ;
        }

        /**
         * Builds the ORDER BY part of the query.
         *
         * @param  array  $fields The fields name to order.
         * @param  string $order  The direction of the order
         * @return string          of the ORDER BY query part.
         */
        protected function buildOrderBy( array $fields = array() , $order = self::ASC )
        {
            if( !empty( $fields ) )
            {
                return " ORDER BY " . implode( ', ', $fields ) . " " . $order ;
            }
            return null ;
        }

        /**
         * Builds the WHERE part of the query.
         *
         * @param  array  $fields The fields name for where condition.
         * @return string         of the WHERE query part.
         */
        protected function buildWhere( array $fields = array(), $condition = null )
        {
            if( $condition == null )
            {
                $condition = self::_AND_ ;
            }

            $where = null ;
            if( !empty( $fields ) )
            {
                $where = " WHERE " ;
                $cpt   = 0 ;
                foreach( $fields as $key => $value )
                {
                    $sep    = $cpt > 0 ? " " . $condition . " " : "" ;
                    $where .= $sep . $this->table . ".`" . $key . "` = '" . $this->sanitize( $value ) . "'" ;
                    $cpt ++ ;
                }
            }
            return $where ;
        }

        /**
         * Returns the result of a query.
         *
         * @return mixed the result of a query.
         */
        protected function getResult( $query )
        {
           return $this->connect->getResult( $query ) ;
        }

        /**
         * Returns the current role of the user.
         *
         * @return RoleVO if it exists, null otherwise.
         */
        protected function getRole()
        {
            if( $this->role == null )
            {
                // add the current role of the user connected.
                $user       = SessionManager::read( SessionManager::buildSessionVar( $this->sessionName, SessionManager::USER_INFOS ) ) ;
                $this->role = $user instanceof UserVO ? $user->role : null;
            }
            return $this->role;
        }

        /**
         * Returns the ValueObject of a result query row.
         *
         * @description        Method to override.
         * @param  array       A row datas from the database to format in ValueObject.
         * @return ValueObject of a result query row.
         */
        protected function getVO( array $row )
        {
        }

        /**
         * Checks if the user is authorized to use the credential passed-in.
         *
         * @param  string  $credential
         * @return boolean true        If the user is authorized to use this credential, false otherwise.
         */
        protected function isAuthorized( $credential )
        {
            if( SessionManager::isAuthentified( $this->sessionName ) )
            {
                if( $this->credentialManager instanceof CredentialsManager )
                {
                    return $this->credentialManager->hasCredential( $credential, $this->getRole() ) ;
                }
                else
                {
                    return true ;
                }
            }
            else
            {
                return false;
            }
        }

        /**
         * Returns true if the field passed-in is set with the values following.
         *
         * @param  string|int   $field
         * @return boolean|int
         */
        protected function isTrue( $field = false )
        {
            return in_array( $field, ServicesMysql::$trueValues ) ? true : 0 ;
        }

        /**
         * Validate the model, build and execute the query.
         *
         * @param  Model  $model
         * @param  string $query
         * @return mixed  ErrorsModelVO if the model has not been validated, mysql results otherwise.
         */
        protected function validate( Model $model, $query )
        {
            if( $model->validate() )
            {
                $query = $this->buildQuery( $query, $this->sanitize( $model->vo ) );
                return $this->getResult( $query );
            }
            else
            {
                return new ErrorsModelVO( array( 'errors' => $model->getErrors() ) );
            }
        }
    }
?>